package top.itaso.lib.widget;

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

import androidx.annotation.RequiresApi;

/**
 * @Description: 水波纹：https://blog.csdn.net/willway_wang/article/details/125244145
 * @Author: itsdf07
 * @CreateDate: 2022/6/22
 */
//<top.itaso.lib.widget.WaterWaveLoadingView
//        android:layout_width="wrap_content"
//        android:layout_height="wrap_content"
//        app:layout_constraintBottom_toTopOf="@+id/layout_login"
//        app:layout_constraintEnd_toEndOf="parent"
//        app:layout_constraintStart_toStartOf="parent"
//        app:layout_constraintTop_toTopOf="parent"
//        itaso:res_image="@mipmap/ic_menu_home" />
public class WaterWaveLoadingView extends View {
    private final String TAG = this.getClass().getSimpleName();
    /**
     * Paint.ANTI_ALIAS_FLAG ：抗锯齿标志
     * Paint.FILTER_BITMAP_FLAG : 使位图过滤的位掩码标志
     * Paint.DITHER_FLAG : 使位图进行有利的抖动的位掩码标志
     * Paint.UNDERLINE_TEXT_FLAG : 下划线
     * Paint.STRIKE_THRU_TEXT_FLAG : 中划线
     * Paint.FAKE_BOLD_TEXT_FLAG : 加粗
     * Paint.LINEAR_TEXT_FLAG : 使文本平滑线性扩展的油漆标志
     * Paint.SUBPIXEL_TEXT_FLAG : 使文本的亚像素定位的绘图标志
     * Paint.EMBEDDED_BITMAP_TEXT_FLAG : 绘制文本时允许使用位图字体的绘图标志
     */
    private Paint mWavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    /**
     * 加载水波纹的 Bitmap 图片：由 mResLoadingImage 转成 Bitmap 得到
     */
    private Bitmap mLoadingBitmap;
    /**
     * 加载水波纹的资源图片：外部传入需要绘制的图片，最终转成 Bitmap - mLoadingBitmap 进行渲染
     */
    private int mLoadingImage;

    /**
     * 渲染图片的默认大小：px
     */
    private int MEASURED_DEFAULT_SIZE = 150;

    /**
     * 绘制水波纹的画布：因为水波纹与图片分别用于混合模式的目标和源，所以水波纹与图片不能共用画布，即使用空白的 mWaveBitmap 重新创建一个新的 Canvas 对象
     */
    private Canvas mWaveCanvas;
    private Bitmap mWaveBitmap;

    /**
     * 图形绘制工具：水波纹（贝塞尔曲线）
     */
    private Path mWaterWavePath = new Path();

    /**
     * 波长：相邻波峰或者波谷之间的长度，如第一个波峰与第二个波峰的长度
     */
    private float mWaveLength = 0f;
    /**
     * 振幅：波峰与波谷的差值，值越大，振幅越大
     */
    private float mWaveAmplitude = 0f;
    private float mWaveAmplitudeLight = 0f;
    //水波纹有深浅两种波纹颜色
    /**
     * 水波纹
     */
    private int WAVE_COLOR = Color.parseColor("#E600A2E8");
    /**
     * 浅色水波纹
     */
    private int WAVE_COLOR_LIGHT = Color.parseColor("#9900A2E8");


    private float offsetX = 0f;
    private float offsetXLight = 0f;
    private float waveHeight = 0f;

    /**
     * 组合动画功能
     */
    private AnimatorSet animatorSet = new AnimatorSet();
    private PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);


    public WaterWaveLoadingView(Context context) {
        this(context, null);
    }

    public WaterWaveLoadingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WaterWaveLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray t = getContext().obtainStyledAttributes(attrs, R.styleable.loadingRipple);
        mLoadingImage = t.getResourceId(R.styleable.loadingRipple_res_image, 0);
        Log.d(TAG, "WaterWaveLoadingView: >>>>>>mLoadingImage=" + mLoadingImage);
        if (mLoadingImage == 0) {
            throw new RuntimeException("必须指定加载水波纹的资源图片");
        }

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        Log.v(TAG, "onMeasure: >>>>>>widthMode:" + widthMode + ",widthSize:" + widthSize);
        Log.v(TAG, "onMeasure: >>>>>>heightMode:" + heightMode + ",heightSize:" + heightSize);
        int measuredWidth;
        int measuredHeight;
        int measuredSize;
        if (widthMode == MeasureSpec.EXACTLY) {
            measuredWidth = widthSize;
        } else {
            measuredWidth = MEASURED_DEFAULT_SIZE;
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            measuredHeight = heightSize;
        } else {
            measuredHeight = MEASURED_DEFAULT_SIZE;
        }

        measuredSize = Math.min(measuredWidth, measuredHeight);

        Log.d(TAG, "onMeasure: >>>>>>measuredWidth:" + measuredWidth + ",measuredHeight:" + measuredHeight + ",measuredSize:" + measuredSize);
        setMeasuredDimension(measuredSize, measuredSize);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mLoadingBitmap = getImage(mLoadingImage, w);

        //使用空白的 Bitmap 创建一个新的 Canvas 来绘制水波纹效果
        mWaveBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        mWaveCanvas = new Canvas(mWaveBitmap);

        //设置水波纹的 振幅 与 波长
        mWaveLength = w / 3f;
        mWaveAmplitude = h / 20f;
        mWaveAmplitudeLight = h / 25f;

        Log.d(TAG, "onSizeChanged: >>>>>>w:" + w + ",h:" + h + ",oldw:" + oldw + ",oldh:" + oldh);
        Log.d(TAG, "onSizeChanged: >>>>>>mWaveLength:" + mWaveLength + ",mWaveAmplitude:" + mWaveAmplitude + ",mWaveAmplitudeLight:" + mWaveAmplitudeLight);

        ValueAnimator animator = ObjectAnimator.ofFloat(this, "offsetX", 0f, mWaveLength);
        animator.setDuration(5000l);
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.RESTART);

        ValueAnimator animatorLight = ObjectAnimator.ofFloat(this, "offsetXLight", mWaveLength, 0f);
        animatorLight.setDuration(1000l);
        animatorLight.setInterpolator(new LinearInterpolator());
        animatorLight.setRepeatCount(ValueAnimator.INFINITE);
        animatorLight.setRepeatMode(ValueAnimator.RESTART);

        ValueAnimator animatorHeight = ObjectAnimator.ofFloat(this, "waveHeight", h + Math.max(mWaveAmplitude, mWaveAmplitudeLight), -Math.max(mWaveAmplitude, mWaveAmplitudeLight));
        animatorHeight.setDuration(5000l);//动画执行周期时间
        animatorHeight.setStartDelay(200l);//动画延迟的开始时长
//        插值器类型：setInterpolator   （来自官方文档）
//        AccelerateDecelerateInterpolator：开始和结束的时候慢，中间快
//        AccelerateInterpolator：开始的时候慢，然后加速
//        AnticipateInterpolator：开始先后退，然后向前
//        AnticipateOvershootInterpolator： 开始先后退，然向前到超标，最后回到最终值
//        BounceInterpolator ：最后会反弹
//        CycleInterpolator：动画会重复一定的周期数
//        DecelerateInterpolator：开始快，然后减速
//        LinearInterpolator：变化匀速
//        OvershootInterpolator：到达最终值后超标，再回到最终值
//        TimeInterpolator：用来自定义插值器
        animatorHeight.setInterpolator(new LinearInterpolator());
        animatorHeight.setRepeatCount(ValueAnimator.INFINITE);
        animatorHeight.setRepeatMode(ValueAnimator.RESTART);


        //多个动画一起执行
        animatorSet.playTogether(animator, animatorLight, animatorHeight);
        animatorSet.start();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.v(TAG, "onDraw: >>>>>>");
        canvas.drawBitmap(mLoadingBitmap, 0f, 0f, mWavePaint);
        drawWaveOnWaveBitmap();
        int saveCount = canvas.saveLayer(0f, 0f, getWidth(), getHeight(), mWavePaint);
        canvas.drawBitmap(mWaveBitmap, 0f, 0f, mWavePaint);
        mWavePaint.setXfermode(xfermode);
        canvas.drawBitmap(mLoadingBitmap, 0f, 0f, mWavePaint);
        mWavePaint.setXfermode(null);
        canvas.restoreToCount(saveCount);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.d(TAG, "onDetachedFromWindow: >>>>>>");
        animatorSet.cancel();
    }

    private void drawWaveOnWaveBitmap() {
        mWaveCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
        mWavePaint.setColor(WAVE_COLOR_LIGHT);
        updateWavePath(offsetXLight, mWaveAmplitudeLight);
        mWaveCanvas.drawPath(mWaterWavePath, mWavePaint);
        mWavePaint.setColor(WAVE_COLOR);
        updateWavePath(offsetX, mWaveAmplitude);
        mWaveCanvas.drawPath(mWaterWavePath, mWavePaint);
    }

    private void updateWavePath(float offsetX, float amplitude) {
        mWaterWavePath.reset();
        float initialStart = offsetX - mWaveLength;
        mWaterWavePath.moveTo(initialStart, waveHeight);
        float waveStart = initialStart;
        while (waveStart <= getWidth()) {
            mWaterWavePath.rQuadTo(mWaveLength / 4f, amplitude, mWaveLength / 2f, 0f);
            mWaterWavePath.rQuadTo(mWaveLength / 4f, -amplitude, mWaveLength / 2f, 0f);
            waveStart += mWaveLength;
        }
        mWaterWavePath.lineTo(getWidth(), getHeight());
        mWaterWavePath.lineTo(0, getHeight());
        mWaterWavePath.close();
    }

    private Bitmap getImage(int drawableRes, int requestSize) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        //如果inJustDecoedBounds设置为true的话，解码bitmap时可以只返回其高、宽和Mime类型，而不必为其申请内存，从而节省了内存空间
        options.inJustDecodeBounds = true;//只读取图片，不加载到内存中
        BitmapFactory.decodeResource(getResources(), drawableRes, options);
        options.inTargetDensity = requestSize;
        options.inDensity = options.outWidth;
        options.inJustDecodeBounds = false;////加载到内存中
        return BitmapFactory.decodeResource(getResources(), drawableRes, options);
    }

    public void setOffsetX(float offsetX) {
        this.offsetX = offsetX;
        invalidate();
    }

    public void setOffsetXLight(float offsetXLight) {
        this.offsetXLight = offsetXLight;
        invalidate();
    }

    public void setWaveHeight(float waveHeight) {
        this.waveHeight = waveHeight;
        invalidate();
    }
}
